package com.bluefoxyu.util;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 雪花算法生成唯一自增ID工具类
 */
public class SnowflakeIdGenerator {

    // ==================== 参数说明 ====================
    // 开始时间戳 (2023-01-01 00:00:00)
    private static final long START_TIMESTAMP = 1672502400000L;

    // 每一部分占用的位数
    private static final long SEQUENCE_BITS = 12; // 序列号占用的位数
    private static final long MACHINE_ID_BITS = 5; // 机器标识占用的位数
    private static final long DATACENTER_ID_BITS = 5; // 数据中心标识占用的位数

    // 每一部分的最大值
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); // 序列号最大值
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS); // 机器ID最大值
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); // 数据中心ID最大值

    // 每一部分向左的位移
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;

    // 数据中心ID和机器ID
    private final long datacenterId;
    private final long machineId;

    // 序列号
    private long sequence = 0L;

    // 上一次生成ID的时间戳
    private long lastTimestamp = -1L;

    // 锁对象，保证线程安全
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 构造函数
     *
     * @param datacenterId 数据中心ID (0 ~ 31)
     * @param machineId    机器ID (0 ~ 31)
     */
    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("数据中心ID不能大于 " + MAX_DATACENTER_ID + " 或小于 0");
        }
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("机器ID不能大于 " + MAX_MACHINE_ID + " 或小于 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    /**
     * 获取下一个唯一ID
     *
     * @return 唯一ID
     */
    public synchronized long nextId() {
        lock.lock();
        try {
            long currentTimestamp = System.currentTimeMillis();

            // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回退了
            if (currentTimestamp < lastTimestamp) {
                throw new RuntimeException("系统时钟回退，拒绝生成ID");
            }

            // 如果是同一毫秒内生成的，则序列号递增
            if (currentTimestamp == lastTimestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                // 序列号溢出，等待下一毫秒
                if (sequence == 0) {
                    currentTimestamp = waitForNextMillis(lastTimestamp);
                }
            } else {
                // 不同毫秒内，序列号重置为0
                sequence = 0L;
            }

            // 更新上一次生成ID的时间戳
            lastTimestamp = currentTimestamp;

            // 生成ID
            return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT) |
                    (datacenterId << DATACENTER_ID_SHIFT) |
                    (machineId << MACHINE_ID_SHIFT) |
                    sequence;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 等待直到下一毫秒
     *
     * @param lastTimestamp 上一次生成ID的时间戳
     * @return 当前时间戳
     */
    private long waitForNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        // 创建一个ID生成器实例，数据中心ID为1，机器ID为1
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);

        // 测试生成10个唯一ID
        for (int i = 0; i < 10; i++) {
            idGenerator.nextId();
            System.out.println("id = " + idGenerator.nextId());
        }
        long end = System.currentTimeMillis();
        long l = end - start;
        System.out.println(l);
    }
}